package com.example.library_core.utils

import com.example.library_core.ext.fromJson
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.TypeAdapter
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import java.lang.reflect.Type

/**
 * gson相关
 */
@Suppress("unused")
object GsonUtils {
    private val gson by lazy { createJson(true) }
    private val gson_no_null by lazy { createJson(false) }
    val gson_with_pretty by lazy { GsonBuilder().setPrettyPrinting().serializeNulls().create() }

    /**
     * Create json
     * @param serializeNulls 用于处理序列化时是否包含null的字段和值 如 "name":null
     * @return
     */
    private fun createJson(serializeNulls: Boolean): Gson {
        val builder = GsonBuilder()
        if (serializeNulls) builder.serializeNulls()
        builder.registerTypeAdapter(object : TypeToken<Map<String, Any>>() {}.type, ObjectTypeAdapterRewrite())
        return builder.create()
    }

    /**
     * 反序列化json
     *
     * @param T  转换的对象
     * @param json 数据
     * @param type object : TypeToken<T>(){}.type
     * @return
     */
    fun <T> fromJson(json: String, type: Type) : T {
       return gson.fromJson(json,type)
    }

    /**
     * 序列化Json
     *
     * @param object 序列化对象
     * @param includeNulls 是否包含空对象
     * @return
     */
    fun toJson(`object`: Any, includeNulls:Boolean = true) : String{
        return if (includeNulls) {
            gson.toJson(`object`)
        } else {
            gson_no_null.toJson(`object`)
        }
    }

}


fun main(){
    val s = "{\"Integer\":\"123\",\"Float\":123.0,\"Long\":123,\"Double\":123.0,\"Boolean\":true,\"String\":\"123\",\"Null\":null}"
    val gson1 = GsonBuilder().create()
    val gson2 = GsonBuilder().registerTypeAdapter(object : TypeToken<Map<String, Any>>(){}.type,ObjectTypeAdapterRewrite()).create()
    println("gson1 --> " + gson1.fromJson(s,object : TypeToken<Map<String, Any>>() {}.type))
    println("gson2 --> " + gson2.fromJson(s,object : TypeToken<Map<String, Any>>() {}.type))
    println("gson3 --> " + s.fromJson(object : TypeToken<Map<String, Any>>() {}.type))
    println("gson4 --> " +  GsonBuilder().serializeNulls().create().fromJson(s,object :TypeToken<Map<String,Any>>(){}.type))
//    gson1 --> {Integer=123, Float=123.0, Long=123.0, Double=123.0, Boolean=true, String=123, Null=null}
//    gson2 --> {Integer=123, Float=123, Long=123, Double=123, Boolean=true, String=123, Null=kotlin.Unit}
//    gson3 --> {Integer=123, Float=123, Long=123, Double=123, Boolean=true, String=123, Null=kotlin.Unit}
//    gson4 --> {Integer=123, Float=123.0, Long=123.0, Double=123.0, Boolean=true, String=123, Null=null}
}

private class ObjectTypeAdapterRewrite : TypeAdapter<Any>() {
    override fun write(out: JsonWriter?, value: Any?) {

    }

    override fun read(inJson: JsonReader?): Any {
        return try {
            return when (inJson?.peek()) {
                JsonToken.BEGIN_ARRAY -> {
                    val list = mutableListOf<Any>()
                    inJson.beginArray()
                    while (inJson.hasNext()) {
                        list.add(read(inJson))
                    }
                    inJson.endArray()
                    list
                }
                JsonToken.BEGIN_OBJECT -> {
                    val map = linkedMapOf<String, Any>()
                    inJson.beginObject()
                    while (inJson.hasNext()) {
                        map[inJson.nextName()] = read(inJson)
                    }
                    inJson.endObject()
                    map
                }
                JsonToken.STRING -> {
                    inJson.nextString()
                }
                JsonToken.NUMBER -> {
                    val doubleNumber = inJson.nextDouble()
                    if (doubleNumber > Long.MAX_VALUE) {
                        return doubleNumber
                    }
                    val longNum = doubleNumber.toLong()
                    if (doubleNumber == longNum.toDouble()) {
                        try {
                            longNum.toInt()
                        } catch (e: Exception) {
                            e.printStackTrace()
                            longNum
                        }
                    } else {
                        doubleNumber
                    }

                }
                JsonToken.BOOLEAN -> {
                    inJson.nextBoolean()
                }
                JsonToken.NULL -> {
                    inJson.nextNull()
                }
                else -> IllegalStateException()
            }
        } catch (e: Exception) {
            e.stackTrace
            IllegalStateException(e)
        }

    }

}